import { PicGo } from 'picgo'
import { IObsConfig } from './config'
// @ts-ignore
import ObsClient from 'esdk-obs-nodejs'

/**
 * 常量配置
 * 集中管理插件中使用的所有常量值
 */
const CONSTANTS = {
  PLUGIN_NAME: 'obs',
  DISPLAY_NAME: '华为云OBS',
  CONFIG_PATH: 'picBed.obs',
  LOG_PREFIX: '[华为云OBS]',
  DEFAULT_CACHE_CONTROL: 'max-age=31536000' // 默认缓存控制，一年
}

/**
 * 日志工具
 * 统一的日志输出接口，便于调试和问题排查
 */
const logger = {
  info: (message: string) => console.log(`${CONSTANTS.LOG_PREFIX} ${message}`),
  error: (message: string, data?: any) => console.error(`${CONSTANTS.LOG_PREFIX} ${message}`, data || ''),
  debug: (message: string, data?: any) => console.log(`${CONSTANTS.LOG_PREFIX} ${message}`, data || '')
}

/**
 * 错误类型定义
 * 定义可能遇到的华为云OBS错误类型，用于更友好的错误提示
 */
const ERROR_TYPES = {
  ACCESS_DENIED: 'Access Denied',
  NO_SUCH_BUCKET: 'NoSuchBucket',
  INVALID_ACCESS_KEY: 'InvalidAccessKeyId',
  SIGNATURE_MISMATCH: 'SignatureDoesNotMatch'
}

/**
 * 处理错误消息
 * 将华为云OBS的错误转换为用户友好的提示信息
 */
const handleError = (error: any): string => {
  const message = error.message || ''

  // 针对不同错误类型提供更友好的错误提示
  const errorMessages = {
    [ERROR_TYPES.ACCESS_DENIED]: '访问被拒绝，请检查：\n1. AccessKey 是否正确\n2. 桶名称是否正确\n3. 地域节点是否正确\n4. AccessKey 是否有权限',
    [ERROR_TYPES.NO_SUCH_BUCKET]: '存储桶不存在，请检查桶名称是否正确',
    [ERROR_TYPES.INVALID_ACCESS_KEY]: 'AccessKey ID 无效',
    [ERROR_TYPES.SIGNATURE_MISMATCH]: 'AccessKey Secret 不正确'
  }

  // 匹配错误类型并返回对应的友好提示
  for (const [errorType, errorMessage] of Object.entries(errorMessages)) {
    if (message.includes(errorType)) {
      return errorMessage
    }
  }

  return message || '未知错误'
}

/**
 * 验证配置项
 * 确保所有必要的配置项都已提供
 */
const validateConfig = (options: any): void => {
  if (!options) {
    throw new Error('找不到华为OBS图床配置文件')
  }

  // 定义必填字段及其显示名称
  const requiredFields = {
    accessKeyId: 'AccessKey ID',
    accessKeySecret: 'AccessKey Secret',
    bucketName: '桶名称',
    endpoint: '地域节点'
  }

  // 检查每个必填字段
  for (const [field, name] of Object.entries(requiredFields)) {
    if (!options[field]) {
      throw new Error(`请配置${name}`)
    }
  }
}

/**
 * 处理单个图片上传
 * 完成单张图片的上传流程
 */
const handleSingleImage = async (ctx: PicGo, obsOptions: IObsConfig, img: any): Promise<void> => {
  logger.info(`开始处理图片: ${img.fileName}`)

  // 验证图片数据完整性
  if (!img.fileName || (!img.buffer && !img.base64Image)) {
    throw new Error('图片数据不完整')
  }

  let image = img.buffer

  // 如果没有buffer但有base64数据，则进行转换
  if (!image && img.base64Image) {
    logger.info('转换base64图片数据为Buffer')
    image = Buffer.from(img.base64Image, 'base64')
  }

  // 初始化 OBS Client
  const obsClient = new ObsClient({
    access_key_id: obsOptions.accessKeyId,
    secret_access_key: obsOptions.accessKeySecret,
    server: obsOptions.endpoint
  })

  const path = obsOptions.path
  const objectKey = `${path ? path + '/' : ''}${img.fileName}`

  logger.info('开始上传图片')

  try {
    const result: any = await obsClient.putObject({
      Bucket: obsOptions.bucketName,
      Key: objectKey,
      Body: image
    })

    if (result.CommonMsg.Status < 300) {
      logger.info('图片上传成功')
      delete img.base64Image
      delete img.buffer

      // 构建图片访问URL
      const domain = obsOptions.customDomain || `https://${obsOptions.bucketName}.${obsOptions.endpoint}`
      img.imgUrl = `${domain}/${encodeURI(objectKey)}`

      logger.debug('生成访问链接:', img.imgUrl)

      // 添加图片处理参数（如果有）
      if (obsOptions.imageProcess) {
        img.imgUrl += obsOptions.imageProcess
        logger.debug('添加图片处理参数后的最终链接:', img.imgUrl)
      }
    } else {
      throw new Error(`上传失败: ${result.CommonMsg.Code} - ${result.CommonMsg.Message}`)
    }
  } catch (error: any) {
    logger.error(`上传出错: ${error.message || error}`)
    throw error
  } finally {
    obsClient.close()
  }
}

/**
 * 处理图片上传的主函数
 * 协调整个上传流程
 */
const handle = async (ctx: PicGo): Promise<PicGo> => {
  logger.info('开始处理上传任务')

  // 获取配置
  const obsOptions = ctx.getConfig<IObsConfig>(CONSTANTS.CONFIG_PATH)

  try {
    // 验证配置
    validateConfig(obsOptions)

    const images = ctx.output
    logger.info(`待上传图片数量: ${images.length}`)

    // 逐个处理图片
    for (const img of images) {
      try {
        await handleSingleImage(ctx, obsOptions, img)
      } catch (error: any) {
        // 单张图片上传失败不影响其他图片
        logger.error(`处理图片时出错: ${error.message}`)
        ctx.emit('notification', {
          title: '图片上传失败',
          body: handleError(error)
        })
      }
    }

    logger.info('所有图片处理完成')
    return ctx
  } catch (err: any) {
    // 处理整体上传过程中的错误
    logger.error(`上传过程发生错误: ${err.message}`)
    logger.error('错误详情:', err)

    ctx.emit('notification', {
      title: '上传失败',
      body: handleError(err)
    })
    throw err
  }
}

/**
 * 插件配置项
 * 定义插件的配置界面
 */
const config = (ctx: PicGo): any[] => {
  // 获取用户已有配置或使用默认值
  const userConfig = ctx.getConfig<IObsConfig>('picBed.obs') || {
    accessKeyId: '',
    accessKeySecret: '',
    bucketName: '',
    endpoint: '',
    path: '',
    imageProcess: '',
    customDomain: '',
    cacheControl: ''
  }

  // 返回配置项定义
  return [
    {
      name: 'accessKeyId',
      type: 'input',
      alias: 'AccessKeyId',
      default: userConfig.accessKeyId || '',
      message: '例如YYTIRPODKPQUFBGNKLPE',
      required: true
    },
    {
      name: 'accessKeySecret',
      type: 'password',
      alias: 'AccessKeySecret',
      default: userConfig.accessKeySecret || '',
      message: '例如sYYTIRPODKPQUFBGNKLPE',
      required: true
    },
    {
      name: 'bucketName',
      type: 'input',
      alias: '桶名称',
      default: userConfig.bucketName || '',
      message: '例如nutpi',
      required: true
    },
    {
      name: 'endpoint',
      type: 'input',
      alias: 'EndPoint',
      default: userConfig.endpoint || '',
      message: '例如obs.cn-north-4.myhuaweicloud.com',
      required: true
    },
    {
      name: 'path',
      type: 'input',
      alias: '存储路径',
      message: '在桶中存储的路径，例如img或img/huawei',
      default: userConfig.path || '',
      required: false
    },
    {
      name: 'imageProcess',
      type: 'input',
      alias: '图片处理参数',
      message: '例如?x-image-process=image/resize,w_800',
      default: userConfig.imageProcess || '',
      required: false
    },
    {
      name: 'customDomain',
      type: 'input',
      alias: '自定义域名',
      message: '例如https://nutpi.net',
      default: userConfig.customDomain || '',
      required: false
    },
    {
      name: 'cacheControl',
      type: 'input',
      alias: 'CacheControl配置',
      message: '例如max-age=31536000',
      default: userConfig.cacheControl || '',
      required: false
    }
  ]
}

/**
 * 插件注册函数
 * 向PicGo注册上传器
 */
export = (ctx: PicGo) => {
  const register = (): void => {
    ctx.helper.uploader.register(CONSTANTS.PLUGIN_NAME, {
      handle,
      name: CONSTANTS.DISPLAY_NAME,
      config
    })
  }

  return {
    uploader: CONSTANTS.PLUGIN_NAME,
    register
  }
}
